﻿using CRL.Core;
using CRL.Core.Extension;
using CRL.RedisProvider;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CRL.Cache
{
    class RedisDelegateCache : DelegateCacheBase
    {
        public RedisDelegateCache() : base(new RedisDelegateCacheContainer())
        {

        }
        static RedisDelegateCache()
        {
            instance = new RedisDelegateCache();
        }
        protected static DelegateCacheBase instance;
        internal static DelegateCacheBase getInstance()
        {
            return instance;
        }
    }
    class RedisDelegateCacheContainer : IDelegateCacheContainer
    {
        static RedisDelegateCacheContainer()
        {
            new ThreadWork().Start("CheckExpiredHashKeys", () =>
            {
                CheckExpiredHashKeys();
                return true;
            }, 60 * 60 * 12);
        }
        static string getName(Type type)
        {
            return type.FullName.StartsWith("System.") ? type.Name : type.FullName;
        }
        static string getTypeName(Type dataType)
        {
            string typeName;
            if (typeof(IEnumerable).IsAssignableFrom(dataType) && dataType != typeof(string))
            {
                var typeFirst = dataType.GenericTypeArguments[0];
                typeName = $"List<{getName(typeFirst)}>";
            }
            else
            {
                typeName = getName(dataType);
            }
            return typeName;
        }
        static string getHashId(Type dataType)
        {
            var typeName = "";
            if (dataType.IsGenericType)
            {
                var definition = dataType.GetGenericTypeDefinition();
                typeName = getName(definition);
                var name2 = dataType.GenericTypeArguments.Select(b => getTypeName(b));
                typeName += $"<{string.Join(",", name2)}>";
            }
            else
            {
                typeName = getTypeName(dataType);
            }
            var hashId = $"delegateCache_{typeName}";
            //Console.WriteLine(hashId);
            return hashId;
        }
        public CacheContainerType CacheContainerType => CacheContainerType.Redis;
        static IRedisClient redisClient;
        static IRedisClient getRedisClient()
        {
            if (redisClient == null)
            {
                redisClient = RedisClientFactory.GetCient();
            }
            return redisClient;
        }
        public bool Get(string key, Type dataType, out cacheDataItem data)
        {
            var client = getRedisClient();
            var hashId = getHashId(dataType);
            return client.HGet<cacheDataItem>(hashId, key, out data);
        }

        public void Write(string key, Type dataType, cacheDataItem data)
        {
            var client = getRedisClient();
            data.data = data.data.ToJson();
            data.key = key;
            var hashId = getHashId(dataType);
            client.HSet(hashId, key, data);
            AddExpireHashKey(client, hashId, key, data.expTime);
        }
        public void Remove(string key, Type dataType)
        {
            var client = getRedisClient();
            var hashId = getHashId(dataType);
            client.HRemove(hashId, key);
        }
        public List<string> GetAllKeys()
        {
            var client = getRedisClient();
            var all = client.SearchKey("delegateCache*");
            var keys = new List<string>();
            foreach (var hashId in all)
            {
                keys.AddRange(client.HGetAllKeys(hashId).Select(b => $"{hashId} {b}"));
            }
            return keys;
        }

        void AddExpireHashKey(IRedisClient client, string hashId, string key, DateTime expireTime)
        {
            client.HSet("ExpireHashKeys", $"{hashId}_{key}", new expireTimeInfo
            {
                hashId = hashId,
                key = key,
                expireTime = expireTime
            });
        }
        static void CheckExpiredHashKeys(int days = 15)
        {
            var client = getRedisClient();
            var all = client.HGetAll<expireTimeInfo>("ExpireHashKeys");
            var time = DateTime.Now.AddDays(-days);
            var list2 = all.Where(b => b.expireTime < time);
            foreach (var item in list2)
            {
                client.HRemove(item.hashId, item.key);
                client.HRemove("ExpireHashKeys", $"{item.hashId}_{item.key}");
            }
            EventLog.Log($"hashId ExpireHashKeys key总数:{all.Count} 过期数:{list2.Count()}", "CheckExpiredHashKeys");
        }
        class expireTimeInfo
        {
            public string hashId;
            public string key;
            public DateTime expireTime;
        }
    }
}
